import { execSync } from 'child_process';
import { consola } from 'consola';
import { startCase } from 'lodash-es';
import path from 'node:path';
import fs from 'fs-extra';
import { fileURLToPath } from 'url';
import packages from '../packages/metadata/packages';
import { version } from '../package.json';
import { build as vite, InlineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import dts from 'vite-plugin-dts';
import icons from 'unplugin-icons/vite';

const __dirname = fileURLToPath(new URL('.', import.meta.url));
const rootDir = path.resolve(__dirname, '..');

// 通过Currying返回rollup external使用的检测函数
// rollup的external不支持通配符，给字符串就是按照死的字符串去对比，函数形式比较灵活
function excludePkgs(pkgs: string[]) {
  return (id: string) => pkgs.some(name => id.startsWith(name));
}

/**
 * 获取包的路径
 * @param pkg
 */
function getDir(pkg: PackageManifest) {
  const root = path.join(rootDir, 'packages', pkg.name);
  const dist = path.join(root, 'dist');
  return { root, dist };
}

/**
 * 构建该包作为npm包发布的package.json文件
 * 将源码库中的package.json稍作修改写入dist目录
 * @param pkg
 */
async function buildMetaFiles(pkg: PackageManifest) {
  const dir = getDir(pkg);
  const packageJSON = await fs.readJSON(path.join(dir.root, 'package.json'));

  // 读取version，然后修改各个子包为一致(为的就是只修改最外层的version即可)
  packageJSON.version = version;
  await fs.writeJSON(path.join(dir.root, 'package.json'), packageJSON, { spaces: 2 });

  // 修改所有子包彼此引用的package版本
  for (const key of Object.keys(packageJSON.dependencies || {}))
    if (key.startsWith('@ink-lake/')) packageJSON.dependencies[key] = version;

  // 写入新的packageJSON文件到dist打包目录
  packageJSON.module = './index.js';
  await fs.writeJSON(path.join(dir.dist, 'package.json'), packageJSON, { spaces: 2 });
}

/**
 * 构建该包输出为esmodule
 * @param pkg
 */
async function buildToESModule(pkg: PackageManifest) {
  const dir = getDir(pkg);
  const outDir = path.join(dir.root, 'dist');
  const entry = path.join(dir.root, 'index.ts');
  const config: InlineConfig = {
    plugins: [
      vue(),
      dts({
        include: `packages/${pkg.name}/**`,
        // insertTypesEntry: true,
        outDir: `packages/${pkg.name}/dist/types`,
        entryRoot: `packages/${pkg.name}`,
      }),
      icons(),
    ],
    build: {
      outDir,
      emptyOutDir: false,
      lib: {
        entry,
        formats: ['es'],
        fileName: 'index',
      },
      cssCodeSplit: true,
      rollupOptions: {
        // 所有用到的库都不打包进去(需要自己手动进行在这里写入)
        // 因为使用es模式默认就会使用包管理工具和编译环境。尽量减小包的体积大小，方便最终摇树优化
        external: excludePkgs(['vue', 'element-plus', 'cropperjs', 'pinia', 'date-fns', 'animejs', 'lodash-es']),
      },
    },
  };

  consola.start(`开始编译[${pkg.name}]模块为esmodule`);
  await vite(config);
  consola.success(`成功输出[${pkg.name}]到${path.join(outDir, 'index.js')}`);
}

/**
 * 构建该包输出为umd
 * @param pkg
 */
async function buildToUMDModule(pkg: PackageManifest) {
  const dir = getDir(pkg);
  const name = pkg.name === 'ink-lake' ? 'Ink' : `Ink${startCase(pkg.name)}`;
  const outDir = path.join(dir.root, 'dist');
  const config: InlineConfig = {
    plugins: [vue(), icons()],
    build: {
      outDir,
      emptyOutDir: false,
      lib: {
        entry: path.join(dir.root, 'index.ts'),
        name,
        formats: ['umd'],
        fileName: 'index',
      },
      cssCodeSplit: false,
      rollupOptions: {
        external: excludePkgs(['vue', 'element-plus', 'pinia', 'cropperjs']),
        output: {
          globals: {
            vue: 'Vue',
            'element-plus': 'ElementPlus',
            pinia: 'Pinia',
            cropperjs: 'Cropperjs',
          },
        },
      },
    },
  };

  consola.start(`开始编译[${pkg.name}]模块为umd`);
  await vite(config);
  consola.info('准备将[.cjs]转为[.js]');
  const filePath = path.join(outDir, 'index.umd.cjs');
  const renamedFilePath = path.join(outDir, 'index.umd.js');
  // 重命名后缀，因为cjs在浏览器中script引入会因为MIME无法识别而无法载入
  fs.rename(filePath, renamedFilePath);
  consola.success(`成功输出[${pkg.name}]到${renamedFilePath}`);
}

async function build() {
  consola.box("There are so many things you don't know.");

  consola.start('清理所有build文件夹');
  execSync('pnpm run clean', { stdio: 'inherit' });
  consola.success('清理完毕\n');

  // @ts-ignore
  for (const pkg of packages.default) {
    consola.info(`构建 ${pkg.name} 包`);
    await buildToESModule(pkg);
    await buildToUMDModule(pkg);
    await buildMetaFiles(pkg);
    consola.log('\n');
  }
}

build();
